#include <stdarg.h>
#include <string.h>
#include <locale.h>
+#include <math.h>
#include <gobject/gvaluecollector.h>
#include <gobject/gobjectnotifyqueue.c>
/* SizeGroup related flags */
guint have_size_groups : 1;
+ guint norender_children : 1;
+ guint norender : 1; /* Don't expose windows, instead recurse via draw */
+
+ guint8 alpha;
+
/* The widget's name. If the widget does not have a name
* (the name is NULL), then its name (as returned by
* "gtk_widget_get_name") is its class's name.
PROP_TOOLTIP_MARKUP,
PROP_TOOLTIP_TEXT,
PROP_WINDOW,
+ PROP_OPACITY,
PROP_DOUBLE_BUFFERED,
PROP_HALIGN,
PROP_VALIGN,
gboolean recurse,
gboolean enabled);
static gboolean event_window_is_still_viewable (GdkEvent *event);
+static void gtk_cairo_set_event (cairo_t *cr,
+ GdkEventExpose *event);
+static void gtk_widget_update_norender (GtkWidget *widget);
/* --- variables --- */
static gpointer gtk_widget_parent_class = NULL;
gpointer invocation_hint,
gpointer marshal_data)
{
+ GtkWidget *widget = g_value_get_object (¶m_values[0]);
+ GdkEventExpose *tmp_event;
+ gboolean push_group;
cairo_t *cr = g_value_get_boxed (¶m_values[1]);
cairo_save (cr);
+ tmp_event = _gtk_cairo_get_event (cr);
+
+ push_group =
+ widget->priv->alpha != 255 &&
+ (!gtk_widget_get_has_window (widget) || tmp_event == NULL);
+
+ if (push_group)
+ {
+ cairo_push_group (cr);
+ gtk_cairo_set_event (cr, NULL);
+ }
_gtk_marshal_BOOLEAN__BOXED (closure,
return_value,
invocation_hint,
marshal_data);
+
+ if (push_group)
+ {
+ cairo_pop_group_to_source (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_paint_with_alpha (cr, widget->priv->alpha / 255.0);
+ }
+
+ gtk_cairo_set_event (cr, tmp_event);
cairo_restore (cr);
}
int n_params,
GType *param_types)
{
+ GtkWidget *widget = GTK_WIDGET (instance);
+ GdkEventExpose *tmp_event;
+ gboolean push_group;
cairo_t *cr;
va_list args_copy;
cr = va_arg (args_copy, gpointer);
cairo_save (cr);
+ tmp_event = _gtk_cairo_get_event (cr);
+
+ push_group =
+ widget->priv->alpha != 255 &&
+ (!gtk_widget_get_has_window (widget) || tmp_event == NULL);
+
+ if (push_group)
+ {
+ cairo_push_group (cr);
+ gtk_cairo_set_event (cr, NULL);
+ }
_gtk_marshal_BOOLEAN__BOXEDv (closure,
return_value,
n_params,
param_types);
+
+ if (push_group)
+ {
+ cairo_pop_group_to_source (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_paint_with_alpha (cr, widget->priv->alpha / 255.0);
+ }
+
+ gtk_cairo_set_event (cr, tmp_event);
cairo_restore (cr);
va_end (args_copy);
FALSE,
GTK_PARAM_READWRITE));
+ /**
+ * GtkWidget:opacity:
+ *
+ * The requested opacity of the widget. See gtk_widget_set_opacity() for
+ * more details about window opacity.
+ *
+ * Before 3.8 this was only availible in GtkWindow
+ *
+ * Since: 3.8
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_OPACITY,
+ g_param_spec_double ("opacity",
+ P_("Opacity for Widget"),
+ P_("The opacity of the widget, from 0 to 1"),
+ 0.0,
+ 1.0,
+ 1.0,
+ GTK_PARAM_READWRITE));
/**
* GtkWidget::show:
* @widget: the object which received the signal.
gtk_widget_set_vexpand (widget, g_value_get_boolean (value));
g_object_thaw_notify (G_OBJECT (widget));
break;
+ case PROP_OPACITY:
+ gtk_widget_set_opacity (widget, g_value_get_double (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
gtk_widget_get_hexpand (widget) &&
gtk_widget_get_vexpand (widget));
break;
+ case PROP_OPACITY:
+ g_value_set_double (value, gtk_widget_get_opacity (widget));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
priv->allocation.y = -1;
priv->allocation.width = 1;
priv->allocation.height = 1;
+ priv->alpha = 255;
priv->window = NULL;
priv->parent = NULL;
g_object_notify_queue_clear (G_OBJECT (widget), nqueue);
g_object_notify_queue_thaw (G_OBJECT (widget), nqueue);
+ gtk_widget_update_norender (widget);
+
gtk_widget_pop_verify_invariants (widget);
g_object_unref (widget);
}
gtk_widget_queue_compute_expand (parent);
}
+ gtk_widget_update_norender (widget);
+
gtk_widget_pop_verify_invariants (widget);
}
if (priv->window != window)
{
priv->window = window;
+
+ if (gtk_widget_get_has_window (widget) && window != NULL && !gdk_window_has_native (window))
+ gdk_window_set_opacity (window,
+ priv->norender ? 0 : priv->alpha / 255.0);
+
g_object_notify (G_OBJECT (widget), "window");
}
}
gdk_window_set_user_data (window, widget);
priv->registered_windows = g_list_prepend (priv->registered_windows, window);
+
+ if (!gtk_widget_get_has_window (widget) && !gdk_window_has_native (window))
+ gdk_window_set_opacity (window,
+ (priv->norender || priv->norender_children) ? 0.0 : 1.0);
}
/**
gdk_window_set_support_multidevice (priv->window, support_multidevice);
}
+static void apply_norender (GtkWidget *widget, gboolean norender);
+
+static void
+apply_norender_cb (GtkWidget *widget, gpointer norender)
+{
+ apply_norender (widget, GPOINTER_TO_INT (norender));
+}
+
+static void
+propagate_norender_non_window (GtkWidget *widget, gboolean norender)
+{
+ GList *l;
+
+ g_assert (!gtk_widget_get_has_window (widget));
+
+ for (l = widget->priv->registered_windows; l != NULL; l = l->next)
+ gdk_window_set_opacity (l->data,
+ norender ? 0 : widget->priv->alpha / 255.0);
+
+ if (GTK_IS_CONTAINER (widget))
+ gtk_container_forall (GTK_CONTAINER (widget), apply_norender_cb,
+ GINT_TO_POINTER (norender));
+}
+
+static void
+apply_norender (GtkWidget *widget, gboolean norender)
+{
+ if (widget->priv->norender == norender)
+ return;
+
+ widget->priv->norender = norender;
+
+ if (gtk_widget_get_has_window (widget))
+ {
+ if (widget->priv->window != NULL)
+ gdk_window_set_opacity (widget->priv->window,
+ norender ? 0 : widget->priv->alpha / 255.0);
+ }
+ else
+ propagate_norender_non_window (widget, norender | widget->priv->norender_children);
+}
+
+/* This is called when the norender_children state of a non-window widget changes,
+ * its parent changes, or its has_window state changes. It means we need
+ * to update the norender of all the windows owned by the widget and those
+ * of child widgets, up to and including the first windowed widgets in the hierarchy.
+ */
+static void
+gtk_widget_update_norender (GtkWidget *widget)
+{
+ gboolean norender;
+ GtkWidget *parent;
+
+ parent = widget->priv->parent;
+
+ norender =
+ parent != NULL &&
+ (parent->priv->norender_children ||
+ (parent->priv->norender && !gtk_widget_get_has_window (parent)));
+
+ apply_norender (widget, norender);
+
+ /* The above may not have propagated to children if norender_children changed but
+ not norender, so we need to enforce propagation. */
+ if (!gtk_widget_get_has_window (widget))
+ propagate_norender_non_window (widget, norender | widget->priv->norender_children);
+}
+
+/**
+ * gtk_widget_set_opacity:
+ * @widget: a #GtkWidget
+ * @opacity: desired opacity, between 0 and 1
+ *
+ * Request the @widget to be rendered partially transparent,
+ * with opacity 0 being fully transparent and 1 fully opaque. (Opacity values
+ * are clamped to the [0,1] range.).
+ * This works on both toplevel widget, and child widgets, although there
+ * are some limitations:
+ *
+ * For toplevel widgets this depends on the capabilities of the windowing
+ * system. On X11 this has any effect only on X screens with a compositing manager
+ * running. See gtk_widget_is_composited(). On Windows it should work
+ * always, although setting a window's opacity after the window has been
+ * shown causes it to flicker once on Windows.
+ *
+ * For child widgets it doesn't work if any affected widget has a native window, or
+ * disables double buffering.
+ *
+ * Since: 3.8
+ **/
+void
+gtk_widget_set_opacity (GtkWidget *widget,
+ gdouble opacity)
+{
+ GtkWidgetPrivate *priv;
+ guint8 alpha;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ priv = widget->priv;
+
+ if (opacity < 0.0)
+ opacity = 0.0;
+ else if (opacity > 1.0)
+ opacity = 1.0;
+
+ alpha = round (opacity * 255);
+ if (alpha == priv->alpha)
+ return;
+
+ priv->alpha = alpha;
+
+ if (gtk_widget_get_has_window (widget))
+ {
+ if (priv->window != NULL)
+ gdk_window_set_opacity (priv->window,
+ priv->norender ? 0 : opacity);
+ }
+ else
+ {
+ /* For non windowed widgets we can't use gdk_window_set_opacity() directly, as there is
+ no GdkWindow at the right place in the hierarchy. For no-window widget this is not a problem,
+ as we just push an opacity group in the draw marshaller.
+
+ However, that only works for non-window descendant widgets. If any descendant has a
+ window that window will not normally be rendered in the draw signal, so the opacity
+ group will not work for it.
+
+ To fix this we set all such windows to a zero opacity, meaning they don't get drawn
+ by gdk, and instead we set a NULL _gtk_cairo_get_event during expose so that the draw
+ handler recurses into windowed widgets.
+
+ We do this by setting "norender_children", which means that any windows in this widget
+ or its ancestors (stopping at the first such windows at each branch in the hierarchy)
+ are set to zero opacity. This is then propagated into all necessary children as norender,
+ which controls whether a window should be exposed or not.
+ */
+ priv->norender_children = priv->alpha != 255;
+ gtk_widget_update_norender (widget);
+ }
+
+ if (gtk_widget_get_realized (widget))
+ gtk_widget_queue_draw (widget);
+}
+
+/**
+ * gtk_widget_get_opacity:
+ * @widget: a #GtkWidget
+ *
+ * Fetches the requested opacity for this widget. See
+ * gtk_widget_set_opacity().
+ *
+ * Return value: the requested opacity for this widget.
+ *
+ * Since: 3.8
+ **/
+gdouble
+gtk_widget_get_opacity (GtkWidget *widget)
+{
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), 0.0);
+
+ return widget->priv->alpha / 255.0;
+}
+
static void
_gtk_widget_set_has_focus (GtkWidget *widget,
gboolean has_focus)
GdkModifierType mnemonic_modifier;
GdkWindowTypeHint gdk_type_hint;
- gdouble opacity;
-
GdkWindow *grip_window;
gchar *startup_id;
guint mnemonics_visible_set : 1;
guint focus_visible : 1;
guint modal : 1;
- guint opacity_set : 1;
guint position : 3;
guint reset_type_hint : 1;
guint resizable : 1;
PROP_GRAVITY,
PROP_TRANSIENT_FOR,
PROP_ATTACHED_TO,
- PROP_OPACITY,
PROP_HAS_RESIZE_GRIP,
PROP_RESIZE_GRIP_VISIBLE,
PROP_APPLICATION,
GTK_TYPE_WIDGET,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
- /**
- * GtkWindow:opacity:
- *
- * The requested opacity of the window. See gtk_window_set_opacity() for
- * more details about window opacity.
- *
- * Since: 2.12
- */
- g_object_class_install_property (gobject_class,
- PROP_OPACITY,
- g_param_spec_double ("opacity",
- P_("Opacity for Window"),
- P_("The opacity of the window, from 0 to 1"),
- 0.0,
- 1.0,
- 1.0,
- GTK_PARAM_READWRITE));
-
/* Style properties.
*/
gtk_widget_class_install_style_property (widget_class,
priv->focus_on_map = TRUE;
priv->deletable = TRUE;
priv->type_hint = GDK_WINDOW_TYPE_HINT_NORMAL;
- priv->opacity = 1.0;
priv->startup_id = NULL;
priv->initial_timestamp = GDK_CURRENT_TIME;
priv->has_resize_grip = TRUE;
case PROP_ATTACHED_TO:
gtk_window_set_attached_to (window, g_value_get_object (value));
break;
- case PROP_OPACITY:
- gtk_window_set_opacity (window, g_value_get_double (value));
- break;
case PROP_HAS_RESIZE_GRIP:
gtk_window_set_has_resize_grip (window, g_value_get_boolean (value));
break;
case PROP_ATTACHED_TO:
g_value_set_object (value, gtk_window_get_attached_to (window));
break;
- case PROP_OPACITY:
- g_value_set_double (value, gtk_window_get_opacity (window));
- break;
case PROP_HAS_RESIZE_GRIP:
g_value_set_boolean (value, priv->has_resize_grip);
break;
* shown causes it to flicker once on Windows.
*
* Since: 2.12
+ * Deprecated: 3.8: Use gtk_widget_set_opacity instead.
**/
void
gtk_window_set_opacity (GtkWindow *window,
gdouble opacity)
{
- GtkWindowPrivate *priv;
-
- g_return_if_fail (GTK_IS_WINDOW (window));
-
- priv = window->priv;
-
- if (opacity < 0.0)
- opacity = 0.0;
- else if (opacity > 1.0)
- opacity = 1.0;
-
- priv->opacity_set = TRUE;
- priv->opacity = opacity;
-
- if (gtk_widget_get_realized (GTK_WIDGET (window)))
- gdk_window_set_opacity (gtk_widget_get_window (GTK_WIDGET (window)),
- priv->opacity);
+ gtk_widget_set_opacity (GTK_WIDGET (window), opacity);
}
/**
* Return value: the requested opacity for this window.
*
* Since: 2.12
+ * Deprecated: 3.8: Use gtk_widget_get_opacity instead.
**/
gdouble
gtk_window_get_opacity (GtkWindow *window)
{
g_return_val_if_fail (GTK_IS_WINDOW (window), 0.0);
- return window->priv->opacity;
+ return gtk_widget_get_opacity (GTK_WIDGET (window));
}
/**
gdk_window = gdk_window_new (parent_window, &attributes, attributes_mask);
gtk_widget_set_window (widget, gdk_window);
- if (priv->opacity_set)
- gdk_window_set_opacity (gdk_window, priv->opacity);
-
gdk_window_enable_synchronized_configure (gdk_window);
gtk_widget_register_window (widget, gdk_window);